module TongLun

using LinearAlgebra
using Libdl

export export_fn, call_fn

# C API structure to hold all function pointers
mutable struct TonglunCAPI
    lib_handle::Ptr{Cvoid}
    
    # Runtime management
    tonglun_create_runtime::Ptr{Cvoid}
    tonglun_run_deferred_free::Ptr{Cvoid}
    
    # Value management
    tonglun_free_value::Ptr{Cvoid}
    
    # Value creation
    tonglun_create_none::Ptr{Cvoid}
    tonglun_create_bool::Ptr{Cvoid}
    tonglun_create_int64::Ptr{Cvoid}
    tonglun_create_float64::Ptr{Cvoid}
    tonglun_create_complex32::Ptr{Cvoid}
    tonglun_create_complex64::Ptr{Cvoid}
    tonglun_create_string_borrowed::Ptr{Cvoid}
    tonglun_create_array::Ptr{Cvoid}
    tonglun_create_tuple::Ptr{Cvoid}
    
    # Value access
    tonglun_value_tag::Ptr{Cvoid}
    tonglun_get_bool::Ptr{Cvoid}
    tonglun_get_int64::Ptr{Cvoid}
    tonglun_get_float64::Ptr{Cvoid}
    tonglun_load_complex32::Ptr{Cvoid}
    tonglun_load_complex64::Ptr{Cvoid}
    tonglun_load_string::Ptr{Cvoid}
    tonglun_get_tuple_length::Ptr{Cvoid}
    tonglun_take_tuple::Ptr{Cvoid}
    tonglun_load_array_info::Ptr{Cvoid}
    
    # Function management
    tonglun_register_function::Ptr{Cvoid}
    tonglun_call_function::Ptr{Cvoid}
    
    # Error handling
    tonglun_try_load_error_message::Ptr{Cvoid}
    tonglun_error_buflen::Ptr{Cvoid}
    tonglun_free_error::Ptr{Cvoid}
    tonglun_binding_error::Ptr{Cvoid}
    
    TonglunCAPI() = new()
end

# Global C API instance
const _CAPI = TonglunCAPI()

# Keep track of objects that need cleanup
const _JULIA_STRING_REFS = Dict{Ptr{Cvoid}, String}()
struct ArrayPool
    data::Vector{Any}
    free_set::Set{Int}
    
    # Initialize with empty vector and set
    ArrayPool() = new(Vector{Any}(), Set{Int}())
end

const _JULIA_ARRAY_REFS = ArrayPool()

function alloc_array_ref(arr::AbstractArray)::Int
    # First try to reuse a freed index
    if !isempty(_JULIA_ARRAY_REFS.free_set)
        idx = pop!(_JULIA_ARRAY_REFS.free_set)
        _JULIA_ARRAY_REFS.data[idx] = arr
        return idx
    end
    
    # Otherwise append to end of vector
    push!(_JULIA_ARRAY_REFS.data, arr)
    return length(_JULIA_ARRAY_REFS.data)
end

function free_array_ref(idx::Int)
    # Validate index
    if idx <= 0 || idx > length(_JULIA_ARRAY_REFS.data)
        return
    end
    
    # Check if already freed
    if idx in _JULIA_ARRAY_REFS.free_set
        return
    end
    
    # Free the reference
    _JULIA_ARRAY_REFS.data[idx] = nothing
    push!(_JULIA_ARRAY_REFS.free_set, idx)
    return
end





# Drop callbacks for non-thread-safe cleanup
function julia_string_drop_callback(foreign_ob::Ptr{Cvoid})
    delete!(_JULIA_STRING_REFS, foreign_ob)
    return nothing
end

function julia_array_drop_callback(foreign_ob::Ptr{Cvoid})
    free_array_ref(reinterpret(Int, foreign_ob))
    return nothing
end

# These will be initialized in __init__()
const STRING_DROP_CALLBACK = Ref{Ptr{Cvoid}}(C_NULL)
const ARRAY_DROP_CALLBACK = Ref{Ptr{Cvoid}}(C_NULL)

# Value type tags matching the C enum
@enum TLValueTag::UInt8 begin
    TL_NONE = 1
    TL_BOOL = 2
    TL_INT64 = 3
    TL_FLOAT64 = 4
    TL_COMPLEX32 = 5
    TL_COMPLEX64 = 6
    TL_STRING = 7
    TL_ARRAY = 8
    TL_HLIST = 9
end

# Array data types matching the C enum
@enum TLArrayDType::Cint begin
    TL_ARRAY_BOOL = 0
    TL_ARRAY_INT8 = 1
    TL_ARRAY_INT16 = 2
    TL_ARRAY_INT32 = 3
    TL_ARRAY_INT64 = 4
    TL_ARRAY_UINT8 = 5
    TL_ARRAY_UINT16 = 6
    TL_ARRAY_UINT32 = 7
    TL_ARRAY_UINT64 = 8
    TL_ARRAY_FLOAT32 = 9
    TL_ARRAY_FLOAT64 = 10
    TL_ARRAY_COMPLEX32 = 11
    TL_ARRAY_COMPLEX64 = 12
end

# Value reference structure matching the C struct
struct TLValue
    tag::UInt8
    data::UInt64
    
    TLValue() = new(UInt8(TL_NONE), UInt64(0))
    TLValue(tag::TLValueTag, data::UInt64) = new(UInt8(tag), data)
end

# Error type
const TLError = UInt64

# Runtime management
mutable struct TonglunRuntime
    id::UInt32
    name::String
    function_refs::Dict{String, Any}  # Keep references to prevent GC
    
    function TonglunRuntime()
        runtime_id = Ref{UInt32}(0)
        name = "julia"
        
        # Use the loaded library handle to identify this runtime
        # This helps with the runtime master-slave problem
        dlhandle = _CAPI.lib_handle
        
        result = ccall(
            _CAPI.tonglun_create_runtime,
            TLError,
            (Ptr{UInt32}, Ptr{Cvoid}, Cstring, UInt8, Ptr{Cvoid}, Ptr{Cvoid}),
            runtime_id,
            dlhandle,
            name,
            UInt8(0),  # Julia 1.9 is not thread-safe in this context
            STRING_DROP_CALLBACK[],  # String drop callback for cleanup
            ARRAY_DROP_CALLBACK[]    # Array drop callback for cleanup
        )
        
        check_error(result)
        
        runtime = new(runtime_id[], name, Dict{String, Any}())
        
        return runtime
    end
end

# Global runtime instance
const _TONGLUN_RUNTIME = Ref{Union{TonglunRuntime, Nothing}}(nothing)

function get_runtime()
    if _TONGLUN_RUNTIME[] === nothing
        _TONGLUN_RUNTIME[] = TonglunRuntime()
    end
    return _TONGLUN_RUNTIME[]
end

# Runtime cleanup function - should be called periodically for non-thread-safe runtimes
function run_deferred_cleanup()
    runtime = get_runtime()
    try
        ccall(
            _CAPI.tonglun_run_deferred_free,
            TLError,
            (UInt32,),
            runtime.id
        )
    catch e
        @warn "Failed to run deferred cleanup: $e"
    end
end

# Error handling
struct TonglunError <: Exception
    message::String
end

function check_error(error_code::TLError)
    if error_code != 0
        # First try with a reasonable buffer size
        buflen = 1024
        buffer = Vector{UInt8}(undef, buflen + 1)
        
        alloc_err = ccall(
            _CAPI.tonglun_try_load_error_message,
            TLError,
            (Ptr{UInt8}, UInt64, TLError),
            buffer,
            UInt64(buflen),
            error_code
        )
        
        if alloc_err == 0
            # Success - find null terminator and create string
            null_pos = findfirst(==(0), buffer)
            msg = String(buffer[1:(null_pos === nothing ? end : null_pos-1)])
        else
            # Buffer too small, get required size
            real_buflen = ccall(
                _CAPI.tonglun_error_buflen,
                UInt64,
                (TLError,),
                alloc_err
            )
            
            # Free the alloc_err
            ccall(
                _CAPI.tonglun_free_error,
                TLError,
                (TLError,),
                alloc_err
            )
            
            if real_buflen == 0
                throw(TonglunError("Failed to load error message [alloc_err = 0]"))
            end
            
            buffer = Vector{UInt8}(undef, real_buflen + 1)
            alloc_err = ccall(
                _CAPI.tonglun_try_load_error_message,
                TLError,
                (Ptr{UInt8}, UInt64, TLError),
                buffer,
                real_buflen,
                error_code
            )
            
            if alloc_err == 0
                null_pos = findfirst(==(0), buffer)
                msg = String(buffer[1:(null_pos === nothing ? end : null_pos-1)])
            else
                throw(TonglunError("Failed to load error message [alloc_err != 0]"))
            end
        end
        
        # Free the original error
        ccall(
            _CAPI.tonglun_free_error,
            TLError,
            (TLError,),
            error_code
        )
        
        throw(TonglunError(msg))
    end
end

# Value creation functions
function create_none()
    value = Ref{TLValue}()
    result = ccall(
        _CAPI.tonglun_create_none,
        TLError,
        (Ptr{TLValue},),
        value
    )
    check_error(result)
    return value[]
end

function create_bool(val::Bool)
    value = Ref{TLValue}()
    result = ccall(
        _CAPI.tonglun_create_bool,
        TLError,
        (Ptr{TLValue}, UInt8),
        value,
        UInt8(val ? 1 : 0)
    )
    check_error(result)
    return value[]
end

function create_int64(val::Int64)
    value = Ref{TLValue}()
    result = ccall(
        _CAPI.tonglun_create_int64,
        TLError,
        (Ptr{TLValue}, Int64),
        value,
        val
    )
    check_error(result)
    return value[]
end

function create_float64(val::Float64)
    value = Ref{TLValue}()
    result = ccall(
        _CAPI.tonglun_create_float64,
        TLError,
        (Ptr{TLValue}, Float64),
        value,
        val
    )
    check_error(result)
    return value[]
end

function create_complex64(val::ComplexF64)
    value = Ref{TLValue}()
    result = ccall(
        _CAPI.tonglun_create_complex64,
        TLError,
        (Ptr{TLValue}, Float64, Float64),
        value,
        real(val),
        imag(val)
    )
    check_error(result)
    return value[]
end

function create_complex32(val::ComplexF32)
    value = Ref{TLValue}()
    result = ccall(
        _CAPI.tonglun_create_complex32,
        TLError,
        (Ptr{TLValue}, Float32, Float32),
        value,
        real(val),
        imag(val)
    )
    check_error(result)
    return value[]
end

function create_string(s::String)
    value = Ref{TLValue}()
    # For Julia strings, we use borrowed strings with cleanup callbacks
    runtime_id = get_runtime().id
    
    # Keep reference to prevent GC
    foreign_ob = pointer_from_objref(s)
    _JULIA_STRING_REFS[foreign_ob] = s
    
    result = ccall(
        _CAPI.tonglun_create_string_borrowed,
        TLError,
        (Ptr{TLValue}, UInt32, Cstring, Ptr{Cvoid}),
        value,
        runtime_id,
        s,
        foreign_ob
    )
    check_error(result)
    return value[]
end

# Array type mapping from Julia to Tonglun
const JULIA_TO_TL_DTYPE = Dict{DataType, String}(
    Bool => "b8",
    Int8 => "i8",
    Int16 => "i16", 
    Int32 => "i32",
    Int64 => "i64",
    UInt8 => "u8",
    UInt16 => "u16",
    UInt32 => "u32", 
    UInt64 => "u64",
    Float32 => "f32",
    Float64 => "f64",
    ComplexF32 => "c32",
    ComplexF64 => "c64"
)

function create_array(arr::AbstractArray{T}) where T
    # Get type code
    typecode = get(JULIA_TO_TL_DTYPE, T, nothing)
    if typecode === nothing
        throw(ArgumentError("Unsupported array element type: $T"))
    end

    # 暂时只支持稠密数组，但已经由 JNumPy 的成功案例，不难优化
    if !(arr isa Array)
        arr = collect(arr)::Array{T}
    end

    
    value = Ref{TLValue}()
    runtime_id = get_runtime().id
    
    # Get array properties
    dims = collect(size(arr))
    strides_bytes = collect(strides(arr)) .* sizeof(T)
    ndims = length(dims)
    
    # Convert to required types
    dims_c = convert(Vector{Csize_t}, dims)
    strides_c = convert(Vector{Cssize_t}, strides_bytes)
    
    # Get data pointer
    data_ptr = pointer(arr)
    
    # Create foreign object reference to keep array alive
    foreign_ob = reinterpret(Ptr{Cvoid}, alloc_array_ref(arr))

    
    result = ccall(
        _CAPI.tonglun_create_array,
        TLError,
        (Ptr{TLValue}, UInt32, Cstring, Ptr{Cvoid}, Int64, Ptr{Csize_t}, Ptr{Cssize_t}, Ptr{Cvoid}, UInt8, UInt8),
        value,
        runtime_id,
        typecode,
        data_ptr,
        Int64(ndims),
        dims_c,
        strides_c,
        foreign_ob,
        UInt8(0),  # not readonly
        UInt8(1)   # Julia arrays are column-major (Fortran order)
    )
    check_error(result)
    return value[]
end

function create_tuple(vals::Union{Tuple, AbstractVector})
    tl_values = [to_value_ref(v) for v in vals]
    value = Ref{TLValue}()
    
    result = ccall(
        _CAPI.tonglun_create_tuple,
        TLError,
        (Ptr{TLValue}, UInt64, Ptr{TLValue}),
        value,
        UInt64(length(tl_values)),
        tl_values
    )
    check_error(result)
    return value[]
end

# Value conversion dispatch
function to_value_ref(val)
    if val === nothing
        return create_none()
    elseif isa(val, Bool)
        return create_bool(val)
    elseif isa(val, Int64)
        return create_int64(val)
    elseif isa(val, Integer)
        return create_int64(Int64(val))
    elseif isa(val, Float64)
        return create_float64(val)
    elseif isa(val, AbstractFloat)
        return create_float64(Float64(val))
    elseif isa(val, ComplexF64)
        return create_complex64(val)
    elseif isa(val, ComplexF32)
        return create_complex32(val)
    elseif isa(val, Complex)
        return create_complex64(ComplexF64(val))
    elseif isa(val, AbstractString)
        return create_string(String(val))
    elseif isa(val, AbstractArray)
        return create_array(val)
    elseif isa(val, Tuple)
        return create_tuple(val)
    else
        throw(ArgumentError("Unsupported Julia type for conversion: $(typeof(val))"))
    end
end

# Value extraction functions
function get_value_tag(value::TLValue)
    tag_int = ccall(
        _CAPI.tonglun_value_tag,
        Cint,
        (Ptr{TLValue},),
        Ref(value)
    )
    return TLValueTag(tag_int)
end

function get_bool(value::TLValue)
    result = ccall(
        _CAPI.tonglun_get_bool,
        UInt8,
        (Ptr{TLValue},),
        Ref(value)
    )
    return result != 0
end

function get_int64(value::TLValue)
    return ccall(
        _CAPI.tonglun_get_int64,
        Int64,
        (Ptr{TLValue},),
        Ref(value)
    )
end

function get_float64(value::TLValue)
    return ccall(
        _CAPI.tonglun_get_float64,
        Float64,
        (Ptr{TLValue},),
        Ref(value)
    )
end

function get_complex32(value::TLValue)
    real_ref = Ref{Float32}()
    imag_ref = Ref{Float32}()
    ccall(
        _CAPI.tonglun_load_complex32,
        Cvoid,
        (Ptr{Float32}, Ptr{Float32}, Ptr{TLValue}),
        real_ref,
        imag_ref,
        Ref(value)
    )
    return ComplexF32(real_ref[], imag_ref[])
end

function get_complex64(value::TLValue)
    real_ref = Ref{Float64}()
    imag_ref = Ref{Float64}()
    ccall(
        _CAPI.tonglun_load_complex64,
        Cvoid,
        (Ptr{Float64}, Ptr{Float64}, Ptr{TLValue}),
        real_ref,
        imag_ref,
        Ref(value)
    )
    # Free the value after loading
    free_value(value)
    return ComplexF64(real_ref[], imag_ref[])
end

function get_string(value::TLValue)
    str_ptr = Ref{Ptr{UInt8}}()
    ccall(
        _CAPI.tonglun_load_string,
        Cvoid,
        (Ptr{Ptr{UInt8}}, Ptr{TLValue}),
        str_ptr,
        Ref(value)
    )
    result = unsafe_string(str_ptr[])
    free_value(value)
    return result
end

# Julia array type mapping from Tonglun to Julia
const TL_TO_JULIA_DTYPE = Dict{TLArrayDType, DataType}(
    TL_ARRAY_BOOL => Bool,
    TL_ARRAY_INT8 => Int8,
    TL_ARRAY_INT16 => Int16,
    TL_ARRAY_INT32 => Int32,
    TL_ARRAY_INT64 => Int64,
    TL_ARRAY_UINT8 => UInt8,
    TL_ARRAY_UINT16 => UInt16,
    TL_ARRAY_UINT32 => UInt32,
    TL_ARRAY_UINT64 => UInt64,
    TL_ARRAY_FLOAT32 => Float32,
    TL_ARRAY_FLOAT64 => Float64,
    TL_ARRAY_COMPLEX32 => ComplexF32,
    TL_ARRAY_COMPLEX64 => ComplexF64
)

function get_array(value::TLValue)
    handle = value.data
    
    # Load array info
    data_ptr = Ref{Ptr{Cvoid}}()
    dims_ptr = Ref{Ptr{Csize_t}}()
    strides_ptr = Ref{Ptr{Cssize_t}}()
    ndims_ref = Ref{Int64}()
    dtype_ref = Ref{TLArrayDType}()
    readonly_ref = Ref{UInt8}()
    fortran_order_ref = Ref{UInt8}()
    
    ccall(
        _CAPI.tonglun_load_array_info,
        Cvoid,
        (Ptr{Ptr{Cvoid}}, Ptr{Ptr{Csize_t}}, Ptr{Ptr{Cssize_t}}, Ptr{Int64}, Ptr{TLArrayDType}, Ptr{UInt8}, Ptr{UInt8}, UInt64),
        data_ptr,
        dims_ptr, 
        strides_ptr,
        ndims_ref,
        dtype_ref,
        readonly_ref,
        fortran_order_ref,
        handle
    )
    
    ndims = ndims_ref[]
    if ndims == 0
        throw(TonglunError("Scalar case not supported"))
    else
        # Extract dimensions and strides
        dims_array = unsafe_wrap(Array, dims_ptr[], ndims)
        # strides_bytes_array = unsafe_wrap(Array, strides_ptr[], ndims)
        dims = Tuple(dims_array)
        # strides_bytes = Tuple(strides_bytes_array)
    end
    
    # Get Julia element type
    element_type = TL_TO_JULIA_DTYPE[dtype_ref[]]
    element_size = sizeof(element_type)
    
    # Convert byte strides to element strides
    # strides_elements = strides_bytes .÷ element_size
    
    # Create Julia array from memory
    # Note: This creates a view of the foreign memory, so we need to be careful about lifetime
    data_typed = Ptr{element_type}(data_ptr[])
    
    if fortran_order_ref[] == 0
        arr = unsafe_wrap(Array, data_typed, reverse(dims))
        if Base.ndims(arr) == 2
            arr = transpose(arr)
        else
            arr = PermutedDimsArray(arr, reverse(1:ndims))
        end
    else
        arr = unsafe_wrap(Array, data_typed, dims)
    end

    # TODO: readonly
    
    return arr
end

function get_tuple(value::TLValue)
    length = ccall(
        _CAPI.tonglun_get_tuple_length,
        UInt64,
        (Ptr{TLValue},),
        Ref(value)
    )
    elements = Vector{TLValue}(undef, length)
    @ccall $(_CAPI.tonglun_take_tuple)(
        pointer(elements)::Ptr{TLValue},
        Ref(value)::Ptr{TLValue}
    )::Cvoid
    parsed_elements = Vector{Any}(undef, length)
    for i in 1:length
        parsed_elements[i] = from_value_ref(elements[i])
    end

    return Tuple(parsed_elements)
end

# Convert TLValue back to Julia value
function from_value_ref(value::TLValue)
    tag = get_value_tag(value)
    
    if tag == TL_NONE
        return nothing
    elseif tag == TL_BOOL
        return get_bool(value)
    elseif tag == TL_INT64
        return get_int64(value)
    elseif tag == TL_FLOAT64
        return get_float64(value)
    elseif tag == TL_COMPLEX32
        return get_complex32(value)
    elseif tag == TL_COMPLEX64
        return get_complex64(value)
    elseif tag == TL_STRING
        return get_string(value)
    elseif tag == TL_ARRAY
        return get_array(value)
    elseif tag == TL_HLIST
        return get_tuple(value)
    else
        throw(TonglunError("Unsupported return type: $tag"))
    end
end

function free_value(value::TLValue)
    ccall(
        _CAPI.tonglun_free_value,
        TLError,
        (Ptr{TLValue},),
        Ref(value)
    )
end

# Global function registry for dispatch by function ID
const _FUNCTION_REGISTRY = Dict{UInt64, Function}()
const _FUNCTION_COUNTER = Ref{UInt64}(0)

# Universal callback function that dispatches based on data (function ID)
function julia_universal_callback(func_id::UInt64, out_ptr::Ptr{TLValue}, args_ptr::Ptr{TLValue}, nargs::UInt64)::UInt64
    try
        # Get the function from registry using func_id
        func = get(_FUNCTION_REGISTRY, func_id, nothing)
        if func === nothing
            if _CAPI.tonglun_binding_error != C_NULL
                error_code = ccall(
                    _CAPI.tonglun_binding_error,
                    TLError,
                    (Cstring, Cstring),
                    "julia",
                    "Function not found in registry: $func_id"
                )
                return error_code
            else
                return UInt64(1)
            end
        end
        
        # Convert arguments from TLValue to Julia values
        args = []
        for i in 1:nargs
            arg_value = unsafe_load(args_ptr, i)
            push!(args, from_value_ref(arg_value))
        end
        
        # Call the Julia function
        result = func(args...)
        
        # Convert result back to TLValue
        result_value = to_value_ref(result)
        unsafe_store!(out_ptr, result_value)
        
        return UInt64(0)  # Success
    catch e
        # Create error during runtime (not precompile time)
        error_msg = string(e)
        if _CAPI.tonglun_binding_error != C_NULL
            error_code = ccall(
                _CAPI.tonglun_binding_error,
                TLError,
                (Cstring, Cstring),
                "julia",
                error_msg
            )
            return error_code
        else
            # Fallback if C API not initialized
            println(stderr, "TongLun error: $error_msg")
            return UInt64(1)
        end
    end
end

# This will be initialized in __init__()
const JULIA_UNIVERSAL_CALLBACK_PTR = Ref{Ptr{Cvoid}}(C_NULL)

# High-level API functions
function export_fn(name::String, func::Function)
    """Export a Julia function to be callable from other languages"""
    # Check if C API is initialized
    if _CAPI.lib_handle == C_NULL
        error("TongLun C API not initialized. Make sure TONGLUN_CORELIB_PATH is set and __init__() has been called.")
    end
    
    runtime = get_runtime()
    
    # Generate unique function ID
    _FUNCTION_COUNTER[] += 1
    func_id = _FUNCTION_COUNTER[]
    
    # Register function in the global registry
    _FUNCTION_REGISTRY[func_id] = func
    
    # Keep reference to prevent GC
    runtime.function_refs[name] = func
    
    # Register function with C library using universal callback and function ID as data
    result = ccall(
        _CAPI.tonglun_register_function,
        TLError,
        (UInt32, Cstring, Ptr{Cvoid}, UInt64),
        runtime.id,
        name,
        JULIA_UNIVERSAL_CALLBACK_PTR[],
        func_id
    )
    check_error(result)
end

function call_fn(name::String, args...)
    """Call a registered function by name"""
    # Convert Julia arguments to TLValue array
    tl_args = [to_value_ref(arg) for arg in args]
    result_value = Ref{TLValue}()
    
    # Call function
    result = ccall(
        _CAPI.tonglun_call_function,
        TLError,
        (Ptr{TLValue}, Cstring, Ptr{TLValue}, UInt64),
        result_value,
        name,
        tl_args,
        UInt64(length(tl_args))
    )
    check_error(result)
    
    return from_value_ref(result_value[])
end


function _julia_eval(x)
    return Base.eval(Main, Meta.parse(x))
end

# Initialize C API and built-in functions
function __init__()
    # Check if libtonglun is already loaded
    lib_handle_addr = get(ENV, "TONGLUN_CORELIB_PTR", nothing)
    if lib_handle_addr === nothing
        corelib_path = get(ENV, "TONGLUN_CORELIB_PATH", nothing)
        if corelib_path === nothing
            error("TONGLUN_CORELIB_PATH environment variable is not set")
        end

        lib_handle = dlopen(corelib_path)
        ENV["TONGLUN_CORELIB_PTR"] = string(reinterpret(UInt64, lib_handle))
    else
        lib_handle = reinterpret(Ptr{Cvoid}, parse(UInt64, lib_handle_addr))
    end
    
    try
        # Load the library
        
        _CAPI.lib_handle = lib_handle
        
        # Initialize all function pointers
        _CAPI.tonglun_create_runtime = dlsym(lib_handle, :tonglun_create_runtime)
        _CAPI.tonglun_run_deferred_free = dlsym(lib_handle, :tonglun_run_deferred_free)
        
        _CAPI.tonglun_free_value = dlsym(lib_handle, :tonglun_free_value)
        
        _CAPI.tonglun_create_none = dlsym(lib_handle, :tonglun_create_none)
        _CAPI.tonglun_create_bool = dlsym(lib_handle, :tonglun_create_bool)
        _CAPI.tonglun_create_int64 = dlsym(lib_handle, :tonglun_create_int64)
        _CAPI.tonglun_create_float64 = dlsym(lib_handle, :tonglun_create_float64)
        _CAPI.tonglun_create_complex32 = dlsym(lib_handle, :tonglun_create_complex32)
        _CAPI.tonglun_create_complex64 = dlsym(lib_handle, :tonglun_create_complex64)
        _CAPI.tonglun_create_string_borrowed = dlsym(lib_handle, :tonglun_create_string_borrowed)
        _CAPI.tonglun_create_array = dlsym(lib_handle, :tonglun_create_array)
        _CAPI.tonglun_create_tuple = dlsym(lib_handle, :tonglun_create_tuple)
        
        _CAPI.tonglun_value_tag = dlsym(lib_handle, :tonglun_value_tag)
        _CAPI.tonglun_get_bool = dlsym(lib_handle, :tonglun_get_bool)
        _CAPI.tonglun_get_int64 = dlsym(lib_handle, :tonglun_get_int64)
        _CAPI.tonglun_get_float64 = dlsym(lib_handle, :tonglun_get_float64)
        _CAPI.tonglun_load_complex32 = dlsym(lib_handle, :tonglun_load_complex32)
        _CAPI.tonglun_load_complex64 = dlsym(lib_handle, :tonglun_load_complex64)
        _CAPI.tonglun_load_string = dlsym(lib_handle, :tonglun_load_string)
        _CAPI.tonglun_get_tuple_length = dlsym(lib_handle, :tonglun_get_tuple_length)
        _CAPI.tonglun_take_tuple = dlsym(lib_handle, :tonglun_take_tuple)
        _CAPI.tonglun_load_array_info = dlsym(lib_handle, :tonglun_load_array_info)
        
        _CAPI.tonglun_register_function = dlsym(lib_handle, :tonglun_register_function)
        _CAPI.tonglun_call_function = dlsym(lib_handle, :tonglun_call_function)
        
        _CAPI.tonglun_try_load_error_message = dlsym(lib_handle, :tonglun_try_load_error_message)
        _CAPI.tonglun_error_buflen = dlsym(lib_handle, :tonglun_error_buflen)
        _CAPI.tonglun_free_error = dlsym(lib_handle, :tonglun_free_error)
        _CAPI.tonglun_binding_error = dlsym(lib_handle, :tonglun_binding_error)
        
        # Initialize callback function pointers
        STRING_DROP_CALLBACK[] = @cfunction(julia_string_drop_callback, Cvoid, (Ptr{Cvoid},))
        ARRAY_DROP_CALLBACK[] = @cfunction(julia_array_drop_callback, Cvoid, (Ptr{Cvoid},))
        JULIA_UNIVERSAL_CALLBACK_PTR[] = @cfunction(julia_universal_callback, UInt64, (UInt64, Ptr{TLValue}, Ptr{TLValue}, UInt64))
        
    catch e
        error("Failed to initialize TongLun library: $e")
    end
    
    TongLun.export_fn("julia.eval", _julia_eval)
end

end # module TongLun
